package com.imooc.business;

import com.imooc.business.constants.RuleVersion;
import com.imooc.business.core.executor.calculate.ExpressionParser;
import com.imooc.business.domain.*;
import com.imooc.business.util.DateUtil;
import com.imooc.infra.mapping.RuleMapping;
import com.imooc.infra.repository.*;
import com.imooc.infra.repository.jdbc.DynamicJdbcRepository;
import com.imooc.infra.repository.jdbc.params.StandardSqlParam;
import com.imooc.infra.repository.jdbc.table.TableExecute;
import com.imooc.module.collect.enums.RuleTypeEnum;
import com.imooc.module.collect.request.AddRuleRequest;
import com.imooc.module.exception.DataCenterException;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author Tim
 * @Description:
 * @Version 1.0
 */
@Slf4j
@Service
public class RuleBusinessImpl implements RuleBusiness {

    @Setter(onMethod_ = @Autowired)
    private RuleRepository ruleRepository;

    @Setter(onMethod_ = @Autowired)
    private CollectRepository collectRepository;

    @Setter(onMethod_ = @Autowired)
    private MetaDataRepository metaDataRepository;

    @Setter(onMethod_ = @Autowired)
    private CalculateRepository calculateRepository;

    @Setter(onMethod_ = @Autowired)
    private OrganRepository organRepository;

    @Setter(onMethod_ = @Autowired)
    private StorageRepository storageRepository;

    @Setter(onMethod_ = @Autowired)
    private RuleMapping ruleMapping;

    @Setter(onMethod_ = @Autowired)
    private TableExecute tableExecute;

    @Setter(onMethod_ = @Autowired)
    private DynamicJdbcRepository dynamicJdbcRepository;

    @Override
    public Rule getRule(String businessId, String ruleType) {
        Rule rule = ruleRepository.getRuleByBusinessIdAndRuleType(businessId, ruleType);
        if (Objects.isNull(rule)) {
            return null;
        }
        Long ruleId = rule.getRuleId();
        List<Collect> collects = collectRepository.listByRuleId(ruleId);
        List<Metadata> metadatas = metaDataRepository.listByRuleId(ruleId);
        List<Calculate> calculates = calculateRepository.listByRuleId(ruleId);
        List<Organ> organs = organRepository.listByRuleId(ruleId);
        Storage storage = storageRepository.getByRuleId(ruleId);

        rule.setStorage(storage);
        rule.setOrgans(organs);
        rule.setCalculates(calculates);
        rule.setMetadatas(metadatas);
        rule.setCollects(collects);
        return rule;
    }

    @Override
    public Boolean addRule(AddRuleRequest request) {
        //获取前端传参
        AddRuleRequest.RuleParam ruleParam = request.getRule();
        //校验参数
        ruleParam.validate();
        ruleParam.setIndexName(ruleParam.getIndexName().toLowerCase());
        Rule rule = convertRule(ruleParam);
        checkSql(rule);

        //获取旧的规则
        Rule oldRule = getRule(ruleParam.getBusinessId(), rule.getRuleType());

        //当前入库的新的规则
        Rule newRule = doAddRule(rule);
        if (Objects.nonNull(newRule)) {
            if (Objects.nonNull(oldRule)) {
                //之前已经存在规则。 之前旧规则有 a,b,c 三个指标名称
                //现在新规则 a,b,c,d,e 五个指标名称。
                //之前的指标名称
                Set<String> oldIndexNames = oldRule.getMetadatas().stream().map(Metadata::getEnName).collect(Collectors.toSet());
                Set<String> newIndexNames = newRule.getMetadatas().stream().map(Metadata::getEnName).collect(Collectors.toSet());
                //需要新增的指标名称字段
                newIndexNames.removeAll(oldIndexNames);

                //需要去添加的指标
                List<Metadata> addMetadata = newRule.getMetadatas().stream().filter(metadata -> newIndexNames.contains(metadata.getEnName())).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(addMetadata)) {
                    //alter table tableName add column columnName
                    List<String> sqlList = tableExecute.updateTableSQL(newRule, addMetadata);
                    dynamicJdbcRepository.execute(oldRule.getStorage().getDataSource(), sqlList);
                }
            } else {
                //创建每一个维度的表
                //之前没有过规则，表明是第一次添加，执行建表
                List<String> sqlList = tableExecute.addTableSQL(newRule, newRule.getMetadatas());
                dynamicJdbcRepository.execute(rule.getStorage().getDataSource(), sqlList);
            }
        }
        return true;
    }

    /**
     * 校验sql
     *
     * @param rule
     */
    private void checkSql(Rule rule) {
        List<Collect> collects = rule.getCollects();
        List<Organ> organs = rule.getOrgans();

        Date startTime = DateUtil.getYesterday();
        Date endTime = new Date();

        if (CollectionUtils.isNotEmpty(collects)) {
            Map<String, Object> params = buildDefaultParams(rule.getBusinessId());
            collects.forEach(collect -> {
                switch (collect.getCollectWay()) {
                    case SQL -> {
                        if (StringUtils.isNotEmpty(collect.getContent())) {
                            params.put(StandardSqlParam.START_TIME, getForamtTime(startTime, collect.getPeriodFormat()));
                            params.put(StandardSqlParam.END_TIME, getForamtTime(endTime, collect.getPeriodFormat()));
                            try {
                                dynamicJdbcRepository.queryMapList(collect.getContent(), collect.getDataSource(), params);
                            } catch (Exception e) {
                                log.error("collects.content sql校验失败, e=", e);
                                throw new DataCenterException("collects.content sql校验失败, error:"+e.getMessage());
                            }
                        }
                    }
                    case INTERFACE -> {

                    }
                }
            });
        }
        if (CollectionUtils.isNotEmpty(organs)) {
            organs.forEach(organ -> {
                switch (organ.getCollectWay()) {
                    case SQL -> {
                        Map<String, Object> params = new HashMap<>();
                        params.put(StandardSqlParam.START_TIME, startTime);
                        params.put(StandardSqlParam.END_TIME, endTime);
                        try {
                            List<Map<String, Object>> organList = dynamicJdbcRepository.queryMapList(organ.getContent(), organ.getDataSource(), params);
                            if (CollectionUtils.isEmpty(organList)) {
                                log.warn("organs.content sql 没有查询到数据, dataSource:{}, sql:{}, params:{}",organ.getDataSource(), organ.getContent(), params);
                            }
                        } catch (Exception e) {
                            log.error("organs.content sql校验失败, dataSource:{}, sql:{}, params:{}, e=", organ.getDataSource(), organ.getContent(), params, e);
                            throw new DataCenterException("organs.content sql校验失败, error:"+e.getMessage());
                        }
                    }
                    case INTERFACE -> {

                    }
                }
            });
        }
    }

    private Object getForamtTime(Date startTime, String periodFormat) {
        if (StringUtils.isEmpty(periodFormat)) {
            return startTime;
        }
        return DateUtil.formatDate(startTime, periodFormat);
    }

    private Map<String, Object> buildDefaultParams(String businessId) {
        Map<String, Object> params = new HashMap<>();
        params.put(StandardSqlParam.USER_IDS, Arrays.asList(1L));
        params.put(StandardSqlParam.GROUP_ID, "abcdd");
        params.put(StandardSqlParam.SCHOOL_ID, 2L);
        params.put(StandardSqlParam.COUNT_TIME, new Date());
        params.put(StandardSqlParam.BUSINESS_ID, businessId);
        return params;
    }

    private Rule doAddRule(Rule rule) {
        Rule saveRule = ruleRepository.save(rule);
        if (Objects.nonNull(saveRule)) {
            List<Organ> organs = organRepository.saveOrgan(saveRule.getRuleId(), rule.getOrgans());
            List<Metadata> metadataList = metaDataRepository.saveMetadata(saveRule.getRuleId(), rule.getMetadatas());
            List<Collect> collects = collectRepository.saveCollect(saveRule.getRuleId(), rule.getCollects());
            List<Calculate> calculates = calculateRepository.saveCalculate(saveRule.getRuleId(), rule.getCalculates());
            Storage storage = storageRepository.saveStorage(saveRule.getRuleId(), rule.getStorage());

            saveRule.setOrgans(organs);
            saveRule.setMetadatas(metadataList);
            saveRule.setCollects(collects);
            saveRule.setCalculates(calculates);
            saveRule.setStorage(storage);
        }
        return saveRule;
    }

    private Rule convertRule(AddRuleRequest.RuleParam ruleParam) {
        Rule rule = ruleMapping.convert(ruleParam);
        rule.setRuleType(RuleTypeEnum.parse(ruleParam.getRuleType()).getSourceType());
        rule.setVersion(DateUtil.formatDate(new Date(), RuleVersion.PATTERN));

        rule.getCalculates().forEach(calculate -> {
            calculate.setExpression(ExpressionParser.parse(calculate));
        });
        return rule;
    }
}
